home *** CD-ROM | disk | FTP | other *** search
- /*
- * CBLibrary - Loader
- * Copyright (C) 2003 Chris Bazley
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /* Multi-dialogue automatic data transfer receiver */
-
- /* 11.09.03 CJB Changed _ldr_check_dropzone() so that NULL_ObjectId rather
- than -1 is the marker of a listener that doesn't care which
- object the file was dragged to. This changes the functional
- definition of loader_register_listener() - sorry.
- loader_buffer_file() now accepts const character strings as
- file_path.
- */
-
- /* ANSI library files */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdbool.h>
-
- /* RISC OS library files */
- #include "kernel.h"
- #include "wimp.h"
- #include "event.h"
- #include "wimplib.h"
- #include "flex.h"
-
- /* Toolbox (for dropzones) */
- #include "toolbox.h"
- #include "iconbar.h"
- #include "gadgets.h"
- #include "window.h"
-
- /* Other headers */
- #include "err.h"
- #include "msgtrans.h"
- #include "hourglass.h"
- #include "Macros.h"
- #include "Loader.h"
- #include "NoBudge.h"
-
- #define RAM_BUFFER_SIZE 256
- #define RAM_BUFFER_EXTEND 256
-
- #define ERR_BAD_OBJECT_ID 0x1b80cb02
- #define ERR_BAD_COMPONENT_ID 0x1b80a914
-
- extern _kernel_oserror shared_err_block;
-
- /* -----------------------------------------------------------------------
- Internal data structures
- */
-
- typedef struct _LoaderListenerBlk
- {
- int flags;
- /* Filetype/destination to listen for */
- int file_type;
- ObjectId object;
- ComponentId *gadgets; /* malloc block, if NO_STATIC_GADGETS_LIST defined */
-
- /* Client participation */
- LoaderFileHandler *loader_method; /* optional */
- LoaderFinishedHandler *finished_method; /* compulsary */
- void *client_handle; /* passed to finished_method */
-
- /* Double-linked list */
- struct _LoaderListenerBlk *next_listener;
- struct _LoaderListenerBlk *prev_listener;
- } LoaderListenerBlk;
-
- /* When listener spawns new dialogue thread: */
- typedef struct _LoaderDialogueBlk
- {
- LoaderListenerBlk *parent_listener;
- int last_message_ref;
- int last_chunk_len;
- WimpMessage *orig_datasave; /* points to malloc block */
- bool RAM_capable;
- int *RAM_buffer; /* flex anchor */
- /* Double-linked list */
- struct _LoaderDialogueBlk *prev_dialogue;
- struct _LoaderDialogueBlk *next_dialogue;
- } LoaderDialogueBlk;
-
- /* -----------------------------------------------------------------------
- Internal function prototypes
- */
-
- static void _ldr_replyto_datasave(WimpMessage *reply_to, LoaderListenerBlk *record_listener);
- static int _ldr_datasave_listener(WimpMessage *message, void *handle);
- static int _ldr_dataloadopen_listener(WimpMessage *message,void *handle);
- static int _ldr_ramfetch_bounce_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle);
- static int _ldr_ramtransmit_handler(WimpMessage *message,void *handle);
- static _kernel_oserror *_ldr_kill_listener(LoaderListenerBlk *kill_listener);
- static LoaderListenerBlk *_ldr_find_broadcast_listener(int file_type);
- static LoaderListenerBlk *_ldr_find_listener(int file_type, ObjectId window, ComponentId *gadgets);
- static LoaderListenerBlk *_ldr_find_suitable_listener(int window_handle, int icon_number, int file_type, int tempfile);
- static bool _ldr_check_dropzone(ObjectId object, ComponentId *gadgets,int window_handle, int icon_number);
- static LoaderDialogueBlk *_ldr_new_thread(void);
- static _kernel_oserror *_ldr_kill_thread(LoaderDialogueBlk *kill_me);
-
- /* -----------------------------------------------------------------------
- Internal library data
- */
-
- static LoaderListenerBlk *listener_list;
- static LoaderDialogueBlk *dialogue_list;
- static int _ldr_flags;
- /*
- From: A.Hodgkinson (andrew.hodgkinson@pace.co.uk)
- Subject: Re: Trouble talking to SaveAs object
- Newsgroups: comp.sys.acorn.programmer
- Date: 2001-06-14 07:50:31 PST
-
- About the best you can do is associate the suggested leafname with
- the relevant document window you're loading into (if that applies)
- and keep a static copy of said leafname. Whenever a new DataSave
- comes along, if you've not already thrown away the old leafname you
- throw it away now. This does create a potential race condition. For
- example, download two files independently via. direct app-to-app save
- and you'd end up with the leafname of the second always being used,
- even if the second download completed first, loading into the
- document window, followed by the first download loading; that is,
- though, rare and not a critical fault. Or, alternatively, you keep
- a record of my_ref too, and in that case at least *one* of the
- simultaneous data transfers will get a decent title from that leaf,
- even if the rest say '<Wimp$Scrap>'.
-
- At worst you will have transient memory claimed for the leaf name
- that will be discarded at the time you send your next DataSave.
-
- Either way, your DataLoad code shouldn't care about recognising
- the my_ref per se - it should load anyway - even though it might
- check if this is non-zero to alter behaviour for app-to-app
- transfers versus original 'from Filer' loads (where my_ref = 0).
- It might use it to try and ensure the right leafname is picked up
- too, but that's not essential.
- */
- static char *datasave_leafname; /* should be initialised to NULL */
- static int datasaveack_myref;
- static LoaderListenerBlk *datasave_listener;
-
- /* -----------------------------------------------------------------------
- Public library functions
- */
-
- _kernel_oserror *loader_initialise(unsigned int flags)
- {
- /* Initialise linked lists */
- listener_list = NULL;
- dialogue_list = NULL;
- _ldr_flags = flags;
-
- datasaveack_myref = -1; /* We don't want our DataLoad handler matching 0 */
-
- /* Set up permanent message handlers for DataSave DataOpen and DataLoad */
- THROW(event_register_message_handler(Wimp_MDataLoad, _ldr_dataloadopen_listener, 0))
- THROW(event_register_message_handler(Wimp_MDataSave, _ldr_datasave_listener, 0))
- if(!FLAG_SET(_ldr_flags, LOADER_IGNOREBCASTS))
- THROW(event_register_message_handler(Wimp_MDataOpen, _ldr_dataloadopen_listener, 0))
-
- /* Ensure that messages are not masked */
- {
- unsigned int mask;
- event_get_mask(&mask);
- CLEAR_FLAG(mask, Wimp_Poll_UserMessageMask);
- CLEAR_FLAG(mask, Wimp_Poll_UserMessageRecordedMask);
- CLEAR_FLAG(mask, Wimp_Poll_UserMessageAcknowledgeMask);
- event_set_mask(mask);
- }
- return NULL; /* success */
- }
-
- /* ----------------------------------------------------------------------- */
- #ifdef INCLUDE_FINALISATION_CODE
- _kernel_oserror *loader_finalise(void)
- {
- /* Kill all listeners */
- {
- LoaderListenerBlk *scan_list = listener_list;
- while(scan_list != NULL) {
- THROW(_ldr_kill_listener(scan_list))
- scan_list = scan_list->next_listener;
- }
- }
-
- /* Remove permanent message handlers */
- THROW(event_deregister_message_handler(Wimp_MDataLoad, _ldr_dataloadopen_listener, 0))
- THROW(event_deregister_message_handler(Wimp_MDataSave, _ldr_datasave_listener, 0))
- if(!FLAG_SET(_ldr_flags, LOADER_IGNOREBCASTS))
- THROW(event_deregister_message_handler(Wimp_MDataOpen, _ldr_dataloadopen_listener, 0))
- return NULL; /* success */
- }
- #endif
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *loader_register_listener(unsigned int flags, int file_type, ObjectId drop_object, ComponentId *drop_gadgets, LoaderFileHandler *loader_method, LoaderFinishedHandler *finished_method, void *client_handle)
- {
- LoaderListenerBlk *newlistener;
- /* Check that proposed key is unique */
- if(_ldr_find_listener(file_type, drop_object, drop_gadgets) != NULL) {
- WRITE_ERR(shared_err_block, "BadListReg");
- return &shared_err_block; /* failure */
- }
- /* Initialise new listener */
- newlistener = malloc(sizeof(LoaderListenerBlk));
- if(newlistener == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* failure */
- }
- /* Set flags */
- newlistener->flags = flags;
-
- /* Set dropzone */
- newlistener->object = drop_object;
-
- #ifdef NO_STATIC_GADGETS_LIST
- /*
- Previously drop_gadgets had to be a static array
- Nowadays we make our own copy just to be on the safe side
- */
- if(drop_gadgets == NULL)
- newlistener->gadgets = NULL;
- else {
- /* count number of gadgets in array */
- int array_len = 0;
- do {
- array_len++;
- } while(drop_gadgets[array_len] != NULL_ComponentId);
-
- /* duplicate the array of gadgets */
- newlistener->gadgets = (ComponentId *)calloc(array_len, sizeof(ComponentId));
- if(newlistener->gadgets == NULL) {
- free(newlistener);
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* failure */
- }
- memcpy(newlistener->gadgets, drop_gadgets, array_len * sizeof(ComponentId));
- }
- #else
- newlistener->gadgets = drop_gadgets;
- #endif
- newlistener->file_type = file_type;
-
- /* Set client handlers */
- newlistener->loader_method = loader_method;
- newlistener->finished_method = finished_method;
- newlistener->client_handle = client_handle;
-
- /* Link this Listener onto front of list */
- newlistener->prev_listener = NULL;
- newlistener->next_listener = listener_list;
- if(listener_list != NULL)
- listener_list->prev_listener = newlistener;
- listener_list = newlistener;
-
- return NULL; /* success */
- }
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *loader_deregister_listener(int file_type, ObjectId drop_object, ComponentId *drop_gadgets)
- {
- LoaderListenerBlk *find_it;
- find_it=_ldr_find_listener(file_type, drop_object, drop_gadgets);
- if(find_it == NULL) {
- WRITE_ERR(shared_err_block, "BadListReg");
- return &shared_err_block; /* failure */
- }
- /* remove the stinking thing */
- if(find_it == datasave_listener) /* CAREFUL!!! */
- datasaveack_myref = -1; /* force re-evaluation of listener */
- return _ldr_kill_listener(find_it);
- }
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *loader_canonicalise(char **buffer, const char *path_var, const char *path_string, const char *file_path)
- {
- /* First pass - determine buffer size needed */
- int spare;
- THROW(_swix(OS_FSControl, _INR(0,5)|_OUT(5), 37, file_path, 0, path_var, path_string, 0, &spare))
-
- char *string = malloc(1 - spare);
- if(string == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block;
- }
-
- /* Second pass - write canonicalised path */
- _kernel_oserror *e = _swix(OS_FSControl, _INR(0,5), 37, file_path, string, path_var, path_string, 1 - spare);
- if(e != NULL)
- free(string);
- else
- *buffer = string;
- return e;
- }
-
- /* ----------------------------------------------------------------------- */
-
- /* We aren't selfish, so we allow access to our generic file loader
- (not quite a LoaderFileHandler because of special provision for sprite files) */
-
- _kernel_oserror *loader_buffer_file(const char *file_path, flex_ptr buffer, bool sprite_file)
- {
- /* Allocate buffer and load raw data into it... */
- _kernel_osfile_block inout;
- int buffer_size, err;
-
- /* Get size of file */
- if(_kernel_osfile(17, file_path, &inout) == _kernel_ERROR)
- return _kernel_last_oserror(); /* failure */
-
- buffer_size = inout.start;
- if(sprite_file)
- buffer_size += sizeof(int);
-
- /* Allocate buffer for data */
- if(!flex_alloc(buffer, buffer_size)) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* failure */
- }
-
- /* Load file */
- hourglass_on();
- inout.load = (int)*buffer; /* load address */
- if(sprite_file)
- inout.load += sizeof(int);
- inout.exec = 0; /* use specified load address */
- err = _kernel_osfile(16, file_path, &inout);
- hourglass_off();
-
- if(err == _kernel_ERROR) {
- flex_free(buffer);
- return _kernel_last_oserror(); /* failure */
- }
-
- if(sprite_file)
- *(int *)*buffer = buffer_size;
-
- return NULL; /* success */
- }
-
- /* -----------------------------------------------------------------------
- Permanent Wimp message listeners
- */
-
- static int _ldr_datasave_listener(WimpMessage *message, void *handle)
- {
- /* We are a permanent DataSave listener (deals with applications) */
- LoaderListenerBlk *listener;
- NOT_USED(handle)
-
- /* This message should come out of the blue */
- if(message->hdr.your_ref != 0) {
- M_RETV("TransUXreq", 0);
- }
-
- /* Are any listeners interested? */
- listener = _ldr_find_suitable_listener(message->data.data_save.destination_window, message->data.data_save.destination_icon, message->data.data_save.file_type, 1);
- if(listener == NULL)
- return 0; /* event not handled */
-
- /* How shall we reply (is RAM transfer allowed?) */
- if(listener->loader_method == NULL) {
- /* Can try RAM transfer (see if they support it) */
-
- /* New message dialogue! */
- LoaderDialogueBlk *newdialogue = _ldr_new_thread();
- if(newdialogue == NULL)
- return 1; /* claim event anyway */
-
- newdialogue->parent_listener = listener;
-
- /* Copy original DataSave message */
- newdialogue->orig_datasave = malloc(message->hdr.size);
- if(newdialogue->orig_datasave == NULL) {
- RG("NoMem"); /* Oh dear, no memory could claimed! */
- goto dialogue_abort;
- }
- memcpy(newdialogue->orig_datasave, message, message->hdr.size);
-
- {
- int initial_bufferlen;
- if(message->data.data_save.estimated_size <= 0) {
- /* Bullshit, so decide buffer size ourself: */
- initial_bufferlen = RAM_BUFFER_SIZE;
- }
- else {
- /* Use estimated file size as RAM buffer size: */
- initial_bufferlen = message->data.data_save.estimated_size;
-
- if(FLAG_SET(newdialogue->parent_listener->flags, LISTENER_SPRITEAREAS)
- && message->data.data_save.file_type == FILETYPE_SPRITE)
- /* (one extra word for sprite area length, written later) */
- initial_bufferlen += sizeof(int);
- }
-
- /* attempt to allocate desired amount of memory */
- if(!flex_alloc((flex_ptr)&newdialogue->RAM_buffer, initial_bufferlen)) {
- RG("NoMem"); /* Oh dear, no memory could claimed! */
- goto dialogue_abort;
- }
-
- /* send RAMFetch message (recorded, in case it bounces) */
- message->hdr.size = 20 + sizeof(WimpRAMFetchMessage);
- message->hdr.your_ref = message->hdr.my_ref;
- message->hdr.action_code = Wimp_MRAMFetch;
- nobudge_register(1024); /* disable flex budging (about to make static copy of pointer) */
- message->data.ram_fetch.buffer = newdialogue->RAM_buffer;
- message->data.ram_fetch.buffer_size = initial_bufferlen;
- }
-
- if(FLAG_SET(newdialogue->parent_listener->flags, LISTENER_SPRITEAREAS)
- && message->data.data_save.file_type == FILETYPE_SPRITE) {
- /* prevent them filling the first word (reserved for sprite area length) */
- message->data.ram_fetch.buffer = (void *)((int)message->data.ram_fetch.buffer + sizeof(int));
- message->data.ram_fetch.buffer_size -= sizeof(int);
- }
-
- if(E(wimp_send_message(Wimp_EUserMessageRecorded, message, message->hdr.sender, NULL, NULL)))
- goto dialogue_abort;
-
- /* We don't actually know that they are RAM transfer capable yet */
- newdialogue->RAM_capable = false;
- newdialogue->last_message_ref = message->hdr.my_ref;
- newdialogue->last_chunk_len = message->data.ram_fetch.buffer_size;
- return 1; /* claim event */
-
- dialogue_abort:
- RE(_ldr_kill_thread(newdialogue));
- return 1; /* claim event */
- }
- else
- /* Must use Scrap transfer protocol */
- _ldr_replyto_datasave(message, listener);
-
- return 1; /* claim event */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static void _ldr_replyto_datasave(WimpMessage *reply_to, LoaderListenerBlk *record_listener)
- {
- /* Remember the leafname, to use when we receive a DataLoad - we DON'T overwrite datasave_leafname yet, as the message may not be sent successfully */
- char *orig_leafname = malloc(strlen(reply_to->data.data_save.leaf_name)+1);
- if(orig_leafname == NULL)
- RG_RET("NoMem");
- strcpy(orig_leafname, reply_to->data.data_save.leaf_name);
-
- /* send DataSaveAck message */
- reply_to->hdr.your_ref = reply_to->hdr.my_ref;
- reply_to->hdr.action_code = Wimp_MDataSaveAck;
- reply_to->hdr.size = (sizeof(WimpMessage) - sizeof(reply_to->data.data_save_ack.leaf_name) + strlen("<Wimp$Scrap>")+1 + 3) & ~3; /* word-align message length */
- /* We are not the Filer, so tell the file's saver that its data is not 'secure', i.e. is not going to end up in a permanent file: */
- strncpy(reply_to->data.data_save_ack.leaf_name, "<Wimp$Scrap>", sizeof(reply_to->data.data_save_ack.leaf_name)-1);
- reply_to->data.data_save_ack.estimated_size = -1;
- if(!E(wimp_send_message(Wimp_EUserMessage, reply_to, reply_to->hdr.sender, NULL, NULL))) {
-
- /* record claiming listener & leafname with my_ref to match */
- datasaveack_myref = reply_to->hdr.my_ref;
- datasave_listener = record_listener;
- free(datasave_leafname);datasave_leafname = orig_leafname;
- }
- }
-
- /* ----------------------------------------------------------------------- */
-
- static int _ldr_dataloadopen_listener(WimpMessage *message, void *handle)
- {
- /* We are a permanent DataLoad/Open listener (deals with Filer) */
- bool delete_file = false, ack_immediately = false;
- char *title;
- LoaderListenerBlk *found_listener;
- NOT_USED(handle)
-
- /* Is this a general broadcast? */
- if(message->hdr.action_code == Wimp_MDataOpen) {
-
- /* This message should come out of the blue */
- if(message->hdr.your_ref != 0)
- M_RETV("TransUXreq", 0)
-
- found_listener = _ldr_find_broadcast_listener(message->data.data_open.file_type);
- ack_immediately = true;
- title = message->data.data_load.leaf_name;
- }
- else {
-
- /* do we already know the correct listener from a DataSave message? */
- if(message->hdr.your_ref == datasaveack_myref) {
- /* we have a leafname and listener for this message */
- datasaveack_myref = -1; /* clear for next time */
- title = datasave_leafname;
- found_listener = datasave_listener;
- }
- else {
- /* We have no original leafname or listener for this message */
- found_listener = _ldr_find_suitable_listener(message->data.data_load.destination_window, message->data.data_save.destination_icon, message->data.data_load.file_type, message->hdr.your_ref);
-
- /* If no proper leafname then substitute 'Untitled' for '<Wimp$Scrap>' */
- if(message->hdr.your_ref != 0)
- title = "<Untitled>";
- else
- title = message->data.data_load.leaf_name;
- }
- if(message->hdr.your_ref != 0)
- delete_file = true;
- }
- if(found_listener == NULL)
- return 0; /* event not handled */
-
- if(ack_immediately) {
- /* Acknowledge immediately */
- message->hdr.your_ref = message->hdr.my_ref;
- message->hdr.action_code = Wimp_MDataLoadAck;
- if(E(wimp_send_message(Wimp_EUserMessage,message, message->hdr.sender, NULL,NULL)))
- return 1; /* claim event */
- }
-
- /* Call file loader */
- void *input_buffer = NULL;
- _kernel_oserror *err = NULL;
- if(found_listener->loader_method == NULL) {
- if(found_listener->file_type != FILETYPE_APP && found_listener->file_type != FILETYPE_DIR) {
- /* Use standard file loader */
- err = loader_buffer_file(
- message->data.data_load.leaf_name,
- (flex_ptr)&input_buffer,
- (FLAG_SET(found_listener->flags, LISTENER_SPRITEAREAS) && found_listener->file_type == FILETYPE_SPRITE)
- );
- }
- }
- else /* Use client's custom file loader */
- err = found_listener->loader_method(message->data.data_load.leaf_name, (flex_ptr)&input_buffer);
-
- if(err != NULL) {
- /* Loading error */
- err_report(err->errnum, msgs_lookupsub("LoadFail", err->errmess, NULL, NULL, NULL));
- return 1; /* claim event */
- }
-
- /* If follow-on DataLoad message then delete input file (more general than checking for "<wimp$scrap>") */
- if(delete_file) {
- /* OS_FSControl 27 is wipe object(s), flags bit 0 means recurse */
- if(E(_swix(OS_FSControl,_INR(0,1)|_IN(3), 27, message->data.data_load.leaf_name, 1u<<0)))
- goto post_load_error;
- }
-
- if(!ack_immediately) {
- /* Acknowledge loaded successfully (just a courtesy message, so we don't expect a reply) */
- message->hdr.your_ref = message->hdr.my_ref;
- message->hdr.action_code = Wimp_MDataLoadAck;
- if(E(wimp_send_message(Wimp_EUserMessage,message, message->hdr.sender, NULL,NULL)))
- goto post_load_error;
- }
-
- /* File loaded successfully - call finished handler */
- {
- char *full_path;
- if(!delete_file) {
- if(E(loader_canonicalise(&full_path, NULL, NULL, title)))
- goto post_load_error;
- title = full_path;
- }
- found_listener->finished_method(title, !delete_file, (flex_ptr)&input_buffer, message->data.data_load.file_type, found_listener->client_handle);
- if(!delete_file)
- free(full_path);
- }
-
- return 1; /* claim event */
-
- post_load_error:
- if(input_buffer != NULL)
- flex_free((flex_ptr)&input_buffer);
-
- return 1; /* claim event */
- }
-
- /* -----------------------------------------------------------------------
- Transient Wimp message handlers (for RAM transfer threads)
-
- [Message handlers MUST claim messages that are directed at them (return 1), even if an error occurs]
- */
-
- static int _ldr_ramfetch_bounce_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
- {
- /* Loading protocol - No reply to a RAMFetch message of ours */
- LoaderDialogueBlk *talk = (LoaderDialogueBlk *)handle;
- NOT_USED(event_code)
- NOT_USED(id_block)
-
- if(event->user_message_acknowledge.hdr.action_code != Wimp_MRAMFetch
- || event->user_message_acknowledge.hdr.my_ref != talk->last_message_ref)
- return 0; /* if not bounced message of ours, pass event on */
-
- nobudge_deregister(); /* no need to protect RAM buffer anymore */
-
- if (!talk->RAM_capable)
- /* Use FTP instead - reply to original DataSave msg */
- _ldr_replyto_datasave(talk->orig_datasave, talk->parent_listener);
-
- RE(_ldr_kill_thread(talk))
- return 1; /* claim event */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static int _ldr_ramtransmit_handler(WimpMessage *message, void *handle)
- {
- /* Loading protocol - Data has been written to buffer */
- LoaderDialogueBlk *talk = (LoaderDialogueBlk *)handle;
-
- if(message->hdr.your_ref != talk->last_message_ref)
- return 0; /* if not reply to our last message, pass event on */
-
- nobudge_deregister(); /* no need to protect RAM buffer anymore */
-
- if(!talk->RAM_capable)
- /* Right, we have a RAM transfer dialogue */
- talk->RAM_capable=true;
-
- /* Was that the last bufferful? */
- if(message->data.ram_transmit.nbytes < talk->last_chunk_len) {
- /* That was the last RAMTransmit message! :-) */
-
- /* Trim off any excess buffer that wasn't written to */
- if(!flex_extend((flex_ptr)&talk->RAM_buffer, flex_size((flex_ptr)&talk->RAM_buffer) - talk->last_chunk_len + message->data.ram_transmit.nbytes)) {
- RG("NoMem");
- goto cleanexit;
- }
- if(FLAG_SET(talk->parent_listener->flags, LISTENER_SPRITEAREAS))
- /* Fill in first word (sprite area size) of sprite area */
- *talk->RAM_buffer = flex_size((flex_ptr)&talk->RAM_buffer);
-
- /* File loaded successfully - call finished handler */
- talk->parent_listener->finished_method(talk->orig_datasave->data.data_save.leaf_name, false, (flex_ptr)&talk->RAM_buffer, talk->orig_datasave->data.data_save.file_type, talk->parent_listener->client_handle);
-
- /* clean up after this message dialogue */
- RE(_ldr_kill_thread(talk));
- }
- else {
- /* There is more data to come, yet...
- extend the buffer for incoming data - preferably we want 256 bytes extra */
- if(!flex_extend((flex_ptr)&talk->RAM_buffer, flex_size((flex_ptr)&talk->RAM_buffer) + RAM_BUFFER_EXTEND)) {
- RG("NoMem");
- goto cleanexit;
- }
- talk->last_chunk_len = RAM_BUFFER_EXTEND;
-
- /* OK, we have extended the buffer, so now we can send another RAMFetch message (recorded, in case it bounces) */
- message->hdr.size = 20 + sizeof(WimpRAMFetchMessage);
- message->hdr.your_ref = message->hdr.my_ref;
- message->hdr.action_code = Wimp_MRAMFetch;
- /* We want them to write at the end of the current data: */
- nobudge_register(1024); /* disable flex budging (about to make static copy of pointer) */
- message->data.ram_fetch.buffer = (void *)((int)talk->RAM_buffer + flex_size((flex_ptr)&talk->RAM_buffer) - RAM_BUFFER_EXTEND);
- /* They have an extension of 256 bytes: */
- message->data.ram_fetch.buffer_size = RAM_BUFFER_EXTEND;
- if(E(wimp_send_message(Wimp_EUserMessageRecorded, message, message->hdr.sender, NULL, NULL)))
- goto cleanexit;
-
- /* update our thread info block... */
- talk->last_message_ref = message->hdr.my_ref;
-
- } /* was not last bufferful */
- return 1; /* claim event */
-
- cleanexit:
- RE(_ldr_kill_thread(talk)); /* clean up after this message dialogue */
- return 1; /* claim event */
- }
-
- /* -----------------------------------------------------------------------
- Miscellaneous internal functions
- */
-
- static _kernel_oserror *_ldr_kill_listener(LoaderListenerBlk *kill_listener)
- {
- LoaderDialogueBlk *scan_dialogues = dialogue_list;
-
- /* De-link Listener from list */
- if(kill_listener == listener_list)
- listener_list = kill_listener->next_listener;
-
- if(kill_listener->prev_listener != NULL)
- kill_listener->prev_listener->next_listener = kill_listener->next_listener;
-
- if(kill_listener->next_listener != NULL)
- kill_listener->next_listener->prev_listener = kill_listener->prev_listener;
-
- #ifdef NO_STATIC_GADGETS_LIST
- /* Remove array of gadget ids */
- free(kill_listener->gadgets);
- #endif
-
- /* Remove any outstanding message handlers */
- while(scan_dialogues != NULL) {
- if(scan_dialogues->parent_listener == kill_listener) {
- scan_dialogues = scan_dialogues->next_dialogue;
- THROW(_ldr_kill_thread(scan_dialogues));
- }
- else
- scan_dialogues = scan_dialogues->next_dialogue;
- }
-
- /* Destroy it */
- free(kill_listener);
- return NULL; /* success */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static LoaderListenerBlk *_ldr_find_broadcast_listener(int file_type)
- {
- /* Search linked list to find if any Listeners are willing to start a thread to load this filetype */
- LoaderListenerBlk *scan_list = listener_list;
- while(scan_list != NULL) {
- if(FLAG_SET(scan_list->flags, LISTENER_CLAIM) && (scan_list->file_type == file_type || scan_list->file_type == FILETYPE_ALL))
- return scan_list; /* found a listener */
- scan_list = scan_list->next_listener;
- }
- return NULL; /* failure */
- }
-
- /* ----------------------------------------------------------------------- */
-
- LoaderListenerBlk *_ldr_find_listener(int file_type, ObjectId window, ComponentId *gadgets)
- {
- LoaderListenerBlk *scan_list = listener_list;
- while(scan_list != NULL) {
- #ifdef NO_STATIC_GADGETS_LIST
- if(scan_list->object == window
- && scan_list->file_type == file_type) {
- if(gadgets == NULL && scan_list->gadgets == NULL)
- return scan_list; /* got a match */
- else {
- if(gadgets != NULL && scan_list->gadgets != NULL) {
- /* Scan arrays until reach end or no match */
- int i = -1;
- do {
- i++;
- if(gadgets[i] != scan_list->gadgets[i])
- goto next_listener; /* no match */
- } while(gadgets[i] != NULL_ComponentId && scan_list->gadgets[i] != NULL_ComponentId);
- return scan_list; /* matched all the way to the end */
- }
- }
- }
- next_listener:
- #else
- if(scan_list->object == window
- && scan_list->file_type == file_type
- && scan_list->gadgets == gadgets)
- return scan_list; /* got a match */
- #endif
- scan_list = scan_list->next_listener;
- }
- return NULL; /* too bad, can't find it */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static bool _ldr_check_dropzone(ObjectId object, ComponentId *gadgets,int window_handle, int icon_number)
- {
- /* Check the dropzone for this listener */
-
- if(object == NULL_ObjectId)
- return true; /* they don't care which object */
-
- /* Is it a Window object or an Iconbar object? */
- {
- ObjectClass objclass;
- {
- _kernel_oserror *errptr;
- errptr=toolbox_get_object_class(0, object, &objclass);
- if(errptr != NULL) {
- if(errptr->errnum != ERR_BAD_OBJECT_ID)
- err_complain(errptr->errnum, errptr->errmess);
- return false; /* ignore listener if bad object ID */
- }
- }
-
- if(objclass == Iconbar_ObjectClass) {
- /* It is an Iconbar object */
- if(window_handle != -2)
- return false; /* not iconbar drop */
-
- {
- int ls_iconh;
- E_RETV(iconbar_get_icon_handle(0, object, &ls_iconh), false);
- if(icon_number != ls_iconh)
- return false; /* wrong icon */
- }
- return true; /* drop on right iconbar icon */
- }
- }
-
- /* It is a Window object - get Wimp window handle */
- {
- int ls_windowh;
- E_RETV(window_get_wimp_handle(0, object, &ls_windowh), false);
- if(window_handle != ls_windowh)
- return false; /* wrong window handle */
- }
-
- if(gadgets == NULL)
- return true; /* they don't care what gadget */
-
- for(int j = 0;gadgets[j] != NULL_ComponentId;j++) {
- /* get list size */
- int nbytes;
- _kernel_oserror *errptr = gadget_get_icon_list(0, object, gadgets[j], NULL, 0, &nbytes);
- if(errptr != NULL) {
- /* ignore gadget if bad gadget ID */
- if(errptr->errnum != ERR_BAD_COMPONENT_ID)
- err_complain(errptr->errnum, errptr->errmess);
- }
- else {
- /* allocate buffer for list */
- int *icons_list = malloc(nbytes);
- if(icons_list == NULL)
- RG_RETV("NoMem", false);
-
- /* read icon numbers into buffer */
- if(E(gadget_get_icon_list(0, object, gadgets[j], icons_list, nbytes, &nbytes))) {
- free(icons_list);
- return false;
- }
- for (int i = 0;i < (nbytes>>2);i++) {
- if(icon_number == icons_list[i]) {
- free(icons_list);
- return true; /* dropped in right place */
- }
- }
- free(icons_list);
- }
- }
- return false; /* none of those icons */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static LoaderListenerBlk *_ldr_find_suitable_listener(int window_handle, int icon_number, int file_type, int tempfile)
- {
- /* Search linked list to find if any Listeners are willing to start a thread to load this dropzone/filetype/source */
-
- bool filetype_handled = false, dropzone_handled = false;
- {
- LoaderListenerBlk *scan_list = listener_list;
- while(scan_list != NULL) {
- bool good_dropzone = _ldr_check_dropzone(scan_list->object, scan_list->gadgets, window_handle, icon_number);
-
- /* Check the filetype accepted by this listener */
- if(file_type == scan_list->file_type || scan_list->file_type == FILETYPE_ALL) {
- filetype_handled = true;
-
- if(good_dropzone) {
- /* We have found a listener for the dropzone */
-
- if(!FLAG_SET(scan_list->flags, LISTENER_FILEONLY) || !tempfile)
- /* either willing to do !Scrap transfer, or this isn't !Scrap transfer */
- return scan_list; /* this is an entirely suitable listener */
- else {
- /* wot a bummer */
- if(!FLAG_SET(_ldr_flags, LOADER_QUIET_NOTPERM))
- M("FileNotPerm");
- return NULL; /* suitable listener not found */
- }
- }
- }
- if(good_dropzone)
- dropzone_handled = true; /* At least one listener likes it */
-
- scan_list = scan_list->next_listener;
- }
- }
- /* Oh dear, what went wrong?... */
- if(!filetype_handled) {
- if(!FLAG_SET(_ldr_flags,LOADER_QUIET_BADTYPE))
- M("NoSuchFileType");
- return NULL; /* suitable listener not found */
- }
- /* -> There exist dropzones that handle that filetype */
- if(!dropzone_handled) {
- if(!FLAG_SET(_ldr_flags, LOADER_QUIET_BADDROP))
- M("NoSuchDropZone");
- return NULL; /* suitable listener not found */
- }
- /* -> File dropped on valid dropzone, though not necessarily for that filetype */
- if(!FLAG_SET(_ldr_flags, LOADER_QUIET_BADDROP))
- M("WrongZone");
- return NULL; /* suitable listener not found */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static LoaderDialogueBlk *_ldr_new_thread(void)
- {
- /* Allocate data block for new RAM transfer thread and link into list
- Do NOT call _ldr_kill_thread() if this function fails! */
- LoaderDialogueBlk *new_dialogue = malloc(sizeof(LoaderDialogueBlk));
- if(new_dialogue == NULL)
- RG_RETV("NoMem", NULL);
- new_dialogue->RAM_buffer = NULL; /* no flex block here */
- new_dialogue->orig_datasave = NULL; /* no malloc block here */
- new_dialogue->next_dialogue = dialogue_list; /* add to front of list */
-
- dialogue_list = new_dialogue;
- new_dialogue->prev_dialogue = NULL; /* no previous record */
-
- /* Register handlers */
- if(E(event_register_message_handler(Wimp_MRAMTransmit, _ldr_ramtransmit_handler, (void *)new_dialogue)))
- goto cockup;
-
- if(!E(event_register_wimp_handler(-1, Wimp_EUserMessageAcknowledge, _ldr_ramfetch_bounce_handler, (void *)new_dialogue)))
- return new_dialogue; /* success */
-
- /* We virtuously tidy up our own mess */
- event_deregister_message_handler(Wimp_MRAMTransmit, _ldr_ramtransmit_handler, (void *)new_dialogue);
- cockup:
- free(new_dialogue);
- return NULL; /* failure */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static _kernel_oserror *_ldr_kill_thread(LoaderDialogueBlk *kill_me)
- {
- /* Free data block for RAM transfer thread and delink */
- if(kill_me == dialogue_list)
- dialogue_list = kill_me->next_dialogue;
-
- if(kill_me->prev_dialogue != NULL)
- kill_me->prev_dialogue->next_dialogue = kill_me->next_dialogue;
-
- if(kill_me->next_dialogue != NULL)
- kill_me->next_dialogue->prev_dialogue = kill_me->prev_dialogue;
-
- /* Free any associated data blocks */
- free(kill_me->orig_datasave); /* does nothing if NULL pointer */
- if(kill_me->RAM_buffer != NULL)
- flex_free((flex_ptr)&kill_me->RAM_buffer);
- free(kill_me);
-
- /* Deregister handlers */
- THROW(event_deregister_message_handler(Wimp_MRAMTransmit, _ldr_ramtransmit_handler, (void *)kill_me));
- THROW(event_deregister_wimp_handler(-1, Wimp_EUserMessageAcknowledge, _ldr_ramfetch_bounce_handler, (void *)kill_me));
-
- return NULL; /* success */
- }
-